﻿/*
 * Created by SharpDevelop.
 * User: atripathi
 * Date: 8/17/2009
 * Time: 7:23 PM
 * help link: http://msdn.microsoft.com/en-us/vcsharp/aa336746.aspx
 */
using System;
using System.Diagnostics;
using System.Linq;
using System.Collections.Generic;

namespace LinqScrap
{
    class Person
    {
        int _id;
        int _idRole;
        string _lastName;
        string _firstName;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }
        public int IDRole
        {
            get { return _idRole; }
            set { _idRole = value; }
        }
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }
    }
    class Role
    {
        int _id;
        string _roleDescription;
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }
        public string RoleDescription
        {
            get { return _roleDescription; }
            set { _roleDescription = value; }
        }
    }
    class Salary
    {
        int _idPerson;
        int _year;
        double _salary;
        public int IDPerson
        {
            get { return _idPerson; }
            set { _idPerson = value; }
        }
        public int Year
        {
            get { return _year; }
            set { _year = value; }
        }
        public double SalaryYear
        {
            get { return _salary; }
            set { _salary = value; }
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            typeof(Program).GetMethods()
                .Where(m => String.Compare(m.Name, "Main", true) != 0 && m.IsStatic)
                .ToList()
                .ForEach(m =>
                    {
                        Console.WriteLine();
                        ObjectDumper.WriteHeader(m.Name);
                        Console.WriteLine();
                        m.Invoke(null, null);
                    });
            Console.Read();
        }

        public static void SelectManySample()
        {
            List<Person> people = new List<Person> {
                new Person { ID = 1,
                    IDRole = 1,
                    LastName = "Anderson",
                    FirstName = "Brad"},
                new Person { ID = 2,
                    IDRole = 2,
                    LastName = "Gray",
                    FirstName = "Tom"},
                new Person { ID = 3,
                    IDRole = 2,
                    LastName = "Grant",
                    FirstName = "Mary"},
                new Person { ID = 4,
                    IDRole = 3,
                    LastName = "Cops",
                    FirstName = "Gary"}};

            List<Role> roles = new List<Role> {
                new Role { ID = 1, RoleDescription = "Manager" },
                new Role { ID = 2, RoleDescription = "Developer" }};

            //normal linq expression to join two classes
            var query = from p in people
                        where p.ID == 1
                        from r in roles
                        where r.ID == p.IDRole
                        select new
                        {
                            p.FirstName,
                            p.LastName,
                            r.RoleDescription
                        };
            ObjectDumper.Write("Sql style select many", query);
            //above linq with select many projection
            var query2 = people
                .Where(p => p.ID == 1)
                .SelectMany(p => roles
                            .Where(r => r.ID == p.ID)
                            .Select(r => new { p.FirstName, p.LastName, r.RoleDescription })
                           );

            //ObjectDumper.Write(query);
            ObjectDumper.Write("New Syntax SelectMany", query2);
        }

        public static void JoinSample()
        {
            #region setup
            List<Person> people = new List<Person> {
                new Person { ID = 1,
                    IDRole = 1,
                    LastName = "Anderson",
                    FirstName = "Brad"},
                new Person { ID = 2,
                    IDRole = 2,
                    LastName = "Gray",
                    FirstName = "Tom"},
                new Person { ID = 3,
                    IDRole = 2,
                    LastName = "Grant",
                    FirstName = "Mary"},
                new Person { ID = 4,
                    IDRole = 3,
                    LastName = "Cops",
                    FirstName = "Gary"}};

            List<Role> roles = new List<Role> {
                new Role { ID = 1, RoleDescription = "Manager" },
                new Role { ID = 2, RoleDescription = "Developer" }};
            #endregion end setup

            #region Simple Select
            //note that multiple "from" statements result in SelectMany. Confirmed with IL.
            var query = from p in people
                        from r in roles
                        where r.ID == p.IDRole
                        select new
                    {
                        p.FirstName,
                        p.LastName,
                        r.RoleDescription
                    };
            ObjectDumper.Write("OldSyntax", query);
            #endregion

            #region Second approach using select many
            var query2 = people
                .SelectMany(p => roles
                            .Where(r => p.IDRole == r.ID)
                            .Select(r => new
                            {
                                p.FirstName,
                                p.LastName,
                                r.RoleDescription
                            }));
            ObjectDumper.Write("SelectMany", query2);
            #endregion

            #region Final approach using join
            /**
                public static IEnumerable<V> Join<T, U, K, V>(
                this IEnumerable<T> outer,
                IEnumerable<U> inner,
                Func<T, K> outerKeySelector,
                Func<U, K> innerKeySelector,
                Func<T, U, V> resultSelector);
             **/
            var query3 = from p in people
                         join r in roles on p.IDRole equals r.ID
                         select new
                         {
                             p.FirstName,
                             p.LastName,
                             r.RoleDescription
                         };
            ObjectDumper.Write("Join using sql syntax", query3);

            var query4
                = people
                .Join(
                    roles,
                    u => u.IDRole,
                    v => v.ID,
                    (u, v) => new
                    {
                        FirstName = u.FirstName,
                        LastName = u.LastName,
                        RoleDescription = v.RoleDescription
                    }
                   );
            ObjectDumper.Write("Join using new syntax", query4);
            #endregion
        }

        public static void GroupJoinSample()
        {
            #region Setup
            List<Person> people = new List<Person> {
                new Person { ID = 1,
                    IDRole = 1,
                    LastName = "Anderson",
                    FirstName = "Brad"},
                new Person { ID = 2,
                    IDRole = 2,
                    LastName = "Gray",
                    FirstName = "Tom"},
                new Person { ID = 3,
                    IDRole = 2,
                    LastName = "Grant",
                    FirstName = "Mary"},
                new Person { ID = 4,
                    IDRole = 3,
                    LastName = "Cops",
                    FirstName = "Gary"}};
            List<Role> roles = new List<Role> {
                new Role { ID = 1, RoleDescription = "Manager" },
                new Role { ID = 2, RoleDescription = "Developer" }};
            #endregion

            #region SQL Style Join Into i.e. GroupJoin
            var query = from p in people
                        join r in roles on p.IDRole equals r.ID into pr
                        from r in pr.DefaultIfEmpty()
                        select new
                        {
                            p.FirstName,
                            p.LastName,
                            RoleDescription = r == null ?
                                "No Role" : r.RoleDescription
                        };
            ObjectDumper.Write("Sql style groupjoin", query);
            #endregion

            #region New Syntax GroupJoin
            /**
            public static IEnumerable<V> GroupJoin<T, U, K, V>(
            this IEnumerable<T> outer,
            IEnumerable<U> inner,
            Func<T, K> outerKeySelector,
            Func<U, K> innerKeySelector,
            Func<T, IEnumerable<U>, V> resultSelector);
             **/
            var query2 = people
                .GroupJoin
                (
                    roles,
                    p => p.IDRole,
                    r => r.ID,
                    (p, roleGroup) => new
                    {
                        FirstName = p.FirstName,
                        LastName = p.LastName,
                        RoleDescription = (roleGroup.Count() == 0) ?
                            "No Role" :
                            roleGroup.Single(r => r.RoleDescription != null).RoleDescription
                    }
                   );
            ObjectDumper.Write("GroupJoin new Syntax", query2);
            #endregion
        }

        public static void GroupBySample()
        {
            var query = from m in typeof(int).GetMethods()
                        group m by m.Name into gb
                        select new { Name = gb.Key };
            ObjectDumper.Write("GroupBy using SQL syntax", query);

            var query1 = typeof(int).GetMethods()
                .GroupBy(k => k.Name)
                .Select(k => new { Name = k.Key, Overloads = k.Count() });
            ObjectDumper.Write("GroupBy using new syntax", query1);
        }

        public static void OrderBySample()
        {
            var query = from m in typeof(int).GetMethods()
                        orderby m.Name
                        group m by m.Name into gb
                        select new { Name = gb.Key };
            ObjectDumper.Write("OrderBy in sql syntax", query);

            var query1 = typeof(int).GetMethods()
                .GroupBy(k => k.Name)
                .OrderBy(s => s.Key)
                .Select(s => new { Name = s.Key, Overload = s.Count() });
            ObjectDumper.Write("OrderBy in new syntax", query1);

            var query2 = typeof(int).GetMethods()
                .GroupBy(k => k.Name)
                .OrderBy(s => s.Count())
                .Select(s => new { Name = s.Key, Overload = s.Count() });
            ObjectDumper.Write("OrderBy on Overload in new syntax", query2);
        }

        public static void SumSample()
        {
            #region setup
            List<Person> people = new List<Person> {
                new Person { ID = 1,
                    IDRole = 1,
                    LastName = "Anderson",
                    FirstName = "Brad"},
                new Person { ID = 2,
                    IDRole = 2,
                    LastName = "Gray",
                    FirstName = "Tom"},
                new Person { ID = 3,
                    IDRole = 2,
                    LastName = "Grant",
                    FirstName = "Mary"},
                new Person { ID = 4,
                    IDRole = 3,
                    LastName = "Cops",
                    FirstName = "Gary"}};

            List<Salary> salaries = new List<Salary> {
                new Salary { IDPerson = 1,
                    Year = 2004,
                    SalaryYear = 10000.00 },
                new Salary { IDPerson = 1,
                    Year = 2005,
                    SalaryYear = 15000.00 }};
            #endregion

            #region OldSyntax
            var query = from p in people
                        join s in salaries on p.ID equals s.IDPerson
                        select new
                        {
                            p.FirstName,
                            p.LastName,
                            s.SalaryYear
                        };

            var querySum = from q in query
                           group q by q.LastName into gp
                           select new
                           {
                               LastName = gp.Key,
                               TotalSalary = gp.Sum(q => q.SalaryYear)
                           };

            ObjectDumper.Write("Sum in Old Syntax", querySum);
            #endregion

            #region NewSyntax
            var query1 = (people
                .Join(salaries,
                p => p.ID,
                s => s.IDPerson,
                (p, s) => new
                {
                    FirstName = p.FirstName,
                    LastName = p.LastName,
                    Salary = s.SalaryYear
                }))
                .GroupBy(q => q.LastName)
                .Select(k => new
                {
                    LastName = k.Key,
                    TotalSalary = k.Sum(q => q.Salary)
                });
            ObjectDumper.Write("Sun in New Syntax", query1);
            #endregion

        }

        public static void MaxMinAverageSample()
        {
            #region Setup
            List<Person> people = new List<Person> {
            new Person { ID = 1,
            IDRole = 1,
            LastName = "Anderson",
            FirstName = "Brad"},
            new Person { ID = 2,
            IDRole = 2,
            LastName = "Gray",
            FirstName = "Tom"},
            new Person { ID = 3,
            IDRole = 2,
            LastName = "Grant",
            FirstName = "Mary"},
            new Person { ID = 4,
            IDRole = 3,
            LastName = "Cops",
            FirstName = "Gary"}};
            List<Salary> salaries = new List<Salary> {
new Salary { IDPerson = 1, Year = 2004, SalaryYear = 10000.00 },
new Salary { IDPerson = 1, Year = 2005, SalaryYear = 15000.00 }};
            #endregion

            #region OldSyntax
            var query = from p in people
                        join s in salaries on p.ID equals s.IDPerson
                        where p.ID == 1
                        select s.SalaryYear;
            ObjectDumper.Write("MinimumSalary OldSyntax", query.Min());
            ObjectDumper.Write("MaximumSalary OldSyntax", query.Max());
            ObjectDumper.Write("AverageSalary OldSyntax", query.Average());
            #endregion

            #region NewSyntax
            var query1 = people
                .Where(p => p.ID == 1)
                .Join(salaries,
                p => p.ID,
                s => s.IDPerson,
                (p, s) => s.SalaryYear
                );
            ObjectDumper.Write("MinimumSalary NewSyntax", query1.Min());
            ObjectDumper.Write("MaximumSalary NewSyntax", query1.Max());
            ObjectDumper.Write("AverageSalary NewSyntax", query1.Average());
            #endregion
        }

        public static void AggregateSample()
        {
            //Note: Boxing to avoid ambiguity between string,object and object,int overloads for Write.
            int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
            var query = numbers.Aggregate((a, b) => a * b);
            ObjectDumper.Write("Aggregate without seed", (object)query);

            int[] numbers1 = { 9, 3, 5, 4, 2, 6, 7, 1, 8 };
            query = numbers1.Aggregate(5, (a, b) => ((a < b) ? (a * b) : a));
            ObjectDumper.Write("Aggregate with seed", (object)query);
        }

        public static void TakeSkipSample()
        {
            int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
            var query = numbers.Take(5);
            ObjectDumper.Write("Using Take", query);
            Console.WriteLine("Press Enter key to see the other elements...");
            //Console.ReadLine();
            var query2 = numbers.Skip(5);
            ObjectDumper.Write("Using Skip", query2);
        }

        public static void TakeWhileSkipWhileSample()
        {
            int[] numbers = { 9, 3, 5, 4, 2, 6, 7, 1, 8 };
            var query = numbers.TakeWhile((n, index) => n >= index);
            ObjectDumper.Write("TakeWhile", query);
            Console.Write("Press Enter key to see the other elements...");
            //Console.ReadLine();
            var query2 = numbers.SkipWhile((n, index) => n >= index);
            ObjectDumper.Write("SkipWhile", query2);
        }

        #region ToSomeType examples
        public static void ToDictionarySample()
        {
            var q = from m in typeof(int).GetMethods()
                    group m by m.Name into gb
                    select gb;
            var q1 = typeof(int).GetMethods()
                .GroupBy(m => m.Name);
            Dictionary<string, int> d = q.ToDictionary(k => k.Key, k => k.Count());
            ObjectDumper.Write(d);
        }

        public static void ToLookupSample()
        {
            List<Person> people = new List<Person> {
            new Person { ID = 1,
            IDRole = 1,
            LastName = "Anderson",
            FirstName = "Brad"},
            new Person { ID = 2,
            IDRole = 2,
            LastName = "Gray",
            FirstName = "Tom"},
            new Person { ID = 3,
            IDRole = 2,
            LastName = "Grant",
            FirstName = "Mary"},
            new Person { ID = 4,
            IDRole = 3,
            LastName = "Cops",
            FirstName = "Gary"}};
            List<Salary> salaries = new List<Salary> {
            new Salary { IDPerson = 1, Year = 2004, SalaryYear = 10000.00 },
            new Salary { IDPerson = 1, Year = 2005, SalaryYear = 15000.00 }};

            ILookup<string,Salary> lookup = people
                .Where(p => p.ID == 1)
                .SelectMany(p => salaries
                    .Where(s => s.IDPerson == p.ID)).ToLookup(k => k.Year.ToString(), k => k);

            var q1 = people
                .Where(p => p.ID == 1)
                .Join(salaries,
                p => p.ID,
                s => s.IDPerson,
                (p, s) => s);

            ILookup<string,Salary> lookup2 = q1.ToLookup(s => s.Year.ToString(), s => s);
            foreach (var lookupItem in lookup)
            {
                Console.WriteLine("Key: {0}", lookupItem.Key);
                foreach (var item in lookupItem)
                {
                    Console.WriteLine("IDPerson: {0}, Year: {1}", item.IDPerson, item.Year);
                }
            }
//            ObjectDumper.Write("Lookup using new syntax for SelectMany", lookup);
            ObjectDumper.Write("Lookup using new syntax for Join", lookup2);
        }
        #endregion
    }
}